﻿using System;
using System.Collections.Generic;
using System.IO;

namespace Geometry
{
    public class MultiPoint : List<Point>
    {
        #region 构造器
        public MultiPoint()
        {
            string relatePath = @"..\..\Data\PointList.txt";
            string absolutePath = Path.GetFullPath(relatePath);
            ReadDataFromFile(absolutePath);
        }
        #endregion

        #region 方法
        public Point GetPoint(string id)
        {
            Point point = null;
            foreach (Point item in this)
            {
                if (id == item.ID)
                {
                    point = item;
                    break;
                }
            }
            return point;
        }

        public Point GetPoint(double x, double y)
        {
            Point point = null;
            foreach (Point item in this)
            {
                if (x == item.X && y == item.Y)
                {
                    point = item;
                    break;
                }
            }
            return point;
        }

        public List<Point> GetPoints(double x, double y)
        {
            List<Point> points = null;
            foreach (var item in this)
            {
                if (points == null)
                    points = new List<Point>();
                if (x == item.X && y == item.Y)
                    points.Add(item);
            }
            return points;
        }

        public List<Point> GetPoints(params string[] IDs)
        {
            List<Point> points = new List<Point>();
            foreach (var id in IDs)
            {
                foreach (Point point in this)
                {
                    if (id == point.ID)
                        points.Add(point);
                }
            }
            return points;
        }

        public void ReadDataFromFile(string path)
        {
            string[] allLines;
            try
            {
                allLines = File.ReadAllLines(path);
                foreach (string line in allLines)
                {
                    Point point = new Point();
                    string[] lineData = line.Split(' ');
                    point.ID = lineData[0];
                    string[] pointData = lineData[1].Trim('(', ')').Split(',');
                    point.X = double.Parse(pointData[0]);
                    point.Y = double.Parse(pointData[1]);
                    this.Add(point);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        public void WriteLine()
        {
            foreach (Point point in this)
                point.WriteLine();
        }

        #region 最小点对距
        public void GetIntervalIndex(out int startIndex, out int midIndex,
            out int endIndex, double buffer = 0)
        {
            startIndex = 0;
            endIndex = Count - 1;
            if (Count % 2 == 0)
                midIndex = Count / 2;
            else
                midIndex = (Count + 1) / 2; ;
            if (buffer != 0)
            {
                //左缓冲
                for (int i = 1; i < midIndex; i++)
                {
                    double disLeft2Xmiddle = this[midIndex].X
                         - this[midIndex - i].X;
                    if (disLeft2Xmiddle >= buffer)
                    {
                        startIndex = midIndex - i;
                        break;
                    }
                }
                //右缓冲
                for (int i = 1; i < midIndex; i++)
                {
                    double disRigth2Xmiddle = this[midIndex + i].X
                         - this[midIndex].X;
                    if (disRigth2Xmiddle >= buffer)
                    {
                        endIndex = midIndex + i;
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// 获取区间范围最小点对距
        /// </summary>
        /// <param name="start">起点索引</param>
        /// <param name="end">终点索引</param>
        /// <returns>点对距</returns>
        /// <remarks>20200318 刘小贝</remarks>
        public Line GetMinPointsDis(int start, int end)
        {
            Line pointPair = new Line(this[start], this[start + 1]);

            for (int i = start; i < end; i++)
            {
                for (int j = i + 1; j <= end; j++)
                {
                    double dis = this[i].GetDistance(this[j]);
                    if (dis < pointPair.Length)
                        pointPair = new Line(this[i], this[j]);
                }
            }
            return pointPair;
        }

        /// <summary>
        /// 获取点集内最小点对
        /// </summary>
        /// <param name="this">点集</param>
        /// <remarks>分治法</remarks>
        public void PointPairMinDis()
        {
            #region 变量
            int startIndex;
            int midIndex;
            int endIndex;
            double buffer;  //左右区间最小值
            Line pointPair_left = null;
            Line pointPair_mid = null;
            Line pointPair_right = null;
            #endregion

            //排序
            this.Sort((point1, point2) => point1.X.CompareTo(point2.X));
            //获取中线缓冲距离
            this.GetIntervalIndex(out startIndex, out midIndex, out endIndex);
            pointPair_left = GetMinPointsDis(startIndex, midIndex);
            pointPair_right = GetMinPointsDis(midIndex, endIndex);
            if (pointPair_left.Length < pointPair_right.Length)
                buffer = pointPair_left.Length;
            else
                buffer = pointPair_right.Length;
            //获取中线区域最小点对距
            this.GetIntervalIndex(out startIndex, out midIndex, out endIndex, buffer);
            pointPair_mid = this.GetMinPointsDis(startIndex, endIndex);
            //最小点对距
            if (pointPair_left.Length < pointPair_right.Length)
            {
                if (pointPair_mid.Length < pointPair_left.Length)
                    pointPair_mid.WriteLine();
                else
                    pointPair_left.WriteLine();
            }
            else
            {
                if (pointPair_mid.Length < pointPair_right.Length)
                    pointPair_mid.WriteLine();
                else
                    pointPair_right.WriteLine();
            }
        }
        #endregion

        #endregion
    }
}
